﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Data.Linq;

namespace Evaluant.Uss.Linq
{
    public partial class QueryableEussData<TData> : IOrderedQueryable<TData>, IInferrable<TData>
    {
        #region Constructors

        /// <summary>
        /// This constructor is called by the client to create the data source.
        /// </summary>
        public QueryableEussData(Evaluant.Uss.ObjectContext.ObjectContext oc)
        {
            Provider = new EussQueryProvider(oc);
            Expression = Expression.Constant(this);
            ObjectContext = oc;
        }

        /// <summary>
        /// This constructor is called by the client to create the data source.
        /// </summary>
        public QueryableEussData(Evaluant.Uss.ObjectContext.ObjectContext oc, bool detachResult) : this(oc)
        {
            DetachResult = detachResult;
        }

        /// <summary>
        /// This constructor is called by Provider.CreateQuery().
        /// </summary>
        /// <param name="expression"></param>
        public QueryableEussData(EussQueryProvider provider, Expression expression):this(provider.ObjectContext)
        {
            if (provider == null)
            {
                throw new ArgumentNullException("provider");
            }

            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }

            if (!typeof(IQueryable<TData>).IsAssignableFrom(expression.Type))
            {
                throw new ArgumentOutOfRangeException("expression");
            }

            Provider = provider;
            Expression = expression;
        }
        #endregion

        #region Properties

        public IQueryProvider Provider { get; private set; }
        public Expression Expression { get; private set; }
        public Evaluant.Uss.ObjectContext.ObjectContext ObjectContext { get; set; }

        public Type ElementType
        {
            get { return typeof(TData); }
        }

        #endregion

        #region Enumerators
        public IEnumerator<TData> GetEnumerator()
        {
            IEnumerable<TData> result = Provider.Execute<IEnumerable<TData>>(Expression);
            IInferrable<TData> linqQuery = result as IInferrable<TData>;
            
            linqQuery.DetachResult = this.DetachResult;

            return result.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            IEnumerable<TData> result = Provider.Execute<IEnumerable<TData>>(Expression);
            IInferrable<TData> linqQuery = result as IInferrable<TData>;
            
            linqQuery.DetachResult = this.DetachResult;

            return result.GetEnumerator();
        }

        #endregion


        #region IInferrable<TData> Members

        public bool DetachResult 
        {
            get { return ((EussQueryProvider)Provider).DetachResult; }
            set { ((EussQueryProvider)Provider).DetachResult = value; }
        }

        #endregion

        #region IInferrable<TData> Members

        public IInferrable<TData> Infer(Expression<Func<TData, object>> relationship)
        {
            Infer(((System.Linq.Expressions.MemberExpression)(relationship.Body)).Member.Name);
            return this;
        }

        public IInferrable Infer<T>(Expression<Func<T, object>> relationship)
        {
            Infer(((System.Linq.Expressions.MemberExpression)(relationship.Body)).Member.Name);
            return this;
        }

        public IInferrable Infer(string relationship)
        {
            ((EussQueryProvider)Provider).InferredRelationships.Add(relationship);
            return this;
        }

        #endregion
    }
}
